package com.whynot.frame.basic.util;

import net.sf.cglib.beans.BeanCopier;
import org.apache.commons.beanutils.*;
import org.apache.commons.collections.MapUtils;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * BeanUtilsEx扩展工具类
 * 
 * @ClassName: BeanUtilsEx
 * @Description: TODO
 * @author 杨凯
 * @date 2015年6月27日 下午4:15:30
 */
public class BeanUtilsEx extends BeanUtils {
	static {
		ConvertUtils.register(new DateConvert(), Date.class);
		ConvertUtils.register(new DateConvert(), java.sql.Date.class);
	}

	static final Map<String, BeanCopier> BEAN_COPIERS = new HashMap<String, BeanCopier>();

	public static void copyProperties(Object dest, Object orig) {
		try {
			BeanUtils.copyProperties(dest, orig);
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	/**
	 * 当orig对象的该属性值不为空，可将orig对象的属性值赋值到dest,覆盖dest原有值
	 * 
	 * @param dest
	 * @param orig
	 * @throws Exception
	 * @throws Exception
	 */
	public static void mergeProperties(Object dest, Object orig) {
		Assert.notNull(dest, "dest is Null");
		Assert.notNull(orig, "orig is Null");
		try {
			BeanInfo destBeanInfo = Introspector.getBeanInfo(dest.getClass());
			PropertyDescriptor[] propertyDescriptors = destBeanInfo
					.getPropertyDescriptors();
			for (int i = 0; i < propertyDescriptors.length; i++) {
				PropertyDescriptor descriptor = propertyDescriptors[i];
				String propertyName = descriptor.getName();
				if (!propertyName.equals("class")) {
					Object origResult = BeanUtils.getProperty(orig,
							propertyName);
					if (origResult != null ) {
						Method writeMethod = descriptor.getWriteMethod();
						writeMethod.invoke(dest, new Object[] { origResult });
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * @param collection
	 * @param beanField
	 * @param beanValue
	 * @return
	 */
	public static <T> List<T> filterListByBeanProperty(Collection<T> collection,
			String beanField, String beanValue) {
		Assert.notNull(beanField, "beanField param is null");
		Assert.notNull(collection, "collection param is null ");
		Assert.notNull(beanValue, "beanValue param is null ");
		List<T> resultList = new ArrayList<T>();
		for (T t : collection) {
			if (ObjectUtils.isEmpty(t))
				continue;
			String fieldval = null;
			if ("String,Object,Integer,Boolean,Long,Chat,List"
					.contains(t.getClass().getSimpleName())) { // fix
				throw new RuntimeException(
						"the Collection include object not a bean");
			} else if (Map.class.isAssignableFrom(t.getClass())) {
				fieldval = MapUtils.getString(((Map) t), beanField);
			} else {
				fieldval = getProperty(t, beanField);
			}
			if (beanValue.equalsIgnoreCase(fieldval)) {
				resultList.add(t);
			}
		}
		return resultList;
	}

	public static String findValFromMapBean(Map map, String key,
			String fieldName) {
		Assert.notNull(key, "key param is null");
		Assert.notNull(fieldName, "fieldName param is null");
		if (ObjectUtils.isEmpty(map))
			return null;

		StringBuffer content = new StringBuffer();
		Object obj = map.get(key); // 得到下一个元素
		if (ObjectUtils.isNotEmpty(obj)) {
			String fieldval = null;
			if (Map.class.isAssignableFrom(obj.getClass())) {
				fieldval = MapUtils.getString(((Map) obj), fieldName);
			} else if ("String,Object,Integer,Boolean,Long,Chat,List"
					.contains(obj.getClass().getSimpleName())) { // fix
				fieldval = obj.toString();
			} else {
				fieldval = BeanUtilsEx.getProperty(obj, fieldName);
			}
			if (ObjectUtils.isNotEmpty(fieldval)) {
				content.append(
						content.length() == 0 ? fieldval : "," + fieldval);
			}
		}
		return content.toString();

	}

	public static String getProperty(Object bean, String name) {
		try {
			return BeanUtils.getProperty(bean, name);
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * eg. PropertiesParser dbcpPropParser = new
	 * PropertiesParser("application.properties") Properties dbcpProp =
	 * dbcpPropParser.getPropertyGroup("dbcp.", true); logger.debug("dbcpProp :"
	 * + dbcpProp); BeanUtilsEx.setBeanProps(stompBroker, dbcpProp);
	 * 
	 * 将配置文件中的属性赋值给POJO的属性字段
	 * 
	 * @param obj
	 * @param props
	 * @throws Exception
	 */
	public static void setBeanProps(Object obj, Properties props)
			throws Exception {
		Enumeration<Object> keys = props.keys();
		while (keys.hasMoreElements()) {
			String name = (String) keys.nextElement();
			String c = name.substring(0, 1).toUpperCase(Locale.US);
			String methName = "set" + c + name.substring(1);
			Method setMeth = getSetMethod(methName,
					obj.getClass());
			try {
				if (setMeth == null) {
					throw new NoSuchMethodException(
							"No setter for property '" + name + "'");
				}

				Class<?>[] params = setMeth.getParameterTypes();
				if (params.length != 1) {
					throw new NoSuchMethodException(
							"No 1-argument setter for property '" + name + "'");
				}

				// does the property value reference another property's value?
				// If so, swap to look at its value
				PropertiesParser refProps = new PropertiesParser(props);
				String refName = name;

				if (params[0].equals(int.class)) {
					setMeth.invoke(obj, new Object[] { Integer
							.valueOf(refProps.getIntProperty(refName)) });
				} else if (params[0].equals(long.class)) {
					setMeth.invoke(obj, new Object[] {
							Long.valueOf(refProps.getLongProperty(refName)) });
				} else if (params[0].equals(float.class)) {
					setMeth.invoke(obj, new Object[] { Float
							.valueOf(refProps.getFloatProperty(refName)) });
				} else if (params[0].equals(double.class)) {
					setMeth.invoke(obj, new Object[] { Double
							.valueOf(refProps.getDoubleProperty(refName)) });
				} else if (params[0].equals(boolean.class)) {
					setMeth.invoke(obj, new Object[] { Boolean
							.valueOf(refProps.getBooleanProperty(refName)) });
				} else if (params[0].equals(String.class)) {
					setMeth.invoke(obj, new Object[] {
							refProps.getStringProperty(refName) });
				} else {
					throw new NoSuchMethodException(
							"No primitive-type setter for property '" + name
									+ "'");
				}
			} catch (NumberFormatException nfe) {
				throw new Exception("Could not parse property '" + name
						+ "' into correct data type: " + nfe.toString());
			}
		}
	}

	public static <T> Method getSetMethod(String name,
			Class<T> clz) {
		Method[] methods = clz.getDeclaredMethods();
		for (Method method : methods) {
			if (method.getName().equals(name)) {
				return method;
			}
		}
		return null;
	}

	/**
	 * 超高效率复制转换类
	 * 
	 * @param src
	 * @param target
	 * @return
	 */
	public static <T> void copyBean(T src, T target) {
		Assert.notNull(src, "src is Null");
		Assert.notNull(target, "target is Null");
		BeanCopier beanCopier = BeanCopier.create(src.getClass(),
				target.getClass(), false);
		beanCopier.copy(src, target, null);
		String key = src.getClass().getName() + target.getClass().getName();
		BeanCopier copier = null;
		if (!BEAN_COPIERS.containsKey(key)) {
			copier = BeanCopier.create(src.getClass(), target.getClass(),
					false);
			BEAN_COPIERS.put(key, copier);
		} else {
			copier = BEAN_COPIERS.get(key);
		}
		copier.copy(src, target, null);
	}

	public static Map convertBean(Object bean) throws IntrospectionException,
			IllegalAccessException, InvocationTargetException {
		Map returnMap = new HashMap();
		BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());

		PropertyDescriptor[] propertyDescriptors = beanInfo
				.getPropertyDescriptors();
		for (int i = 0; i < propertyDescriptors.length; i++) {
			PropertyDescriptor descriptor = propertyDescriptors[i];
			String propertyName = descriptor.getName();
			if (!propertyName.equals("class")) {
				Method readMethod = descriptor.getReadMethod();
				Object result = readMethod.invoke(bean, new Object[0]);
				if (result != null) {
					returnMap.put(propertyName, result);
				} else {
					returnMap.put(propertyName, "");
				}
			}
		}
		return returnMap;
	}

	/**
	 * 将一个 Map 对象转化为一个 JavaBean
	 * 
	 * @param type
	 *            要转化的类型
	 * @param map
	 *            包含属性值的 map
	 * @return 转化出来的 JavaBean 对象
	 * @throws IntrospectionException
	 *             如果分析类属性失败
	 * @throws IllegalAccessException
	 *             如果实例化 JavaBean 失败
	 * @throws InstantiationException
	 *             如果实例化 JavaBean 失败
	 * @throws InvocationTargetException
	 *             如果调用属性的 setter 方法失败
	 */
	public static Object convertMap(Class type, Map map)
			throws IntrospectionException, IllegalAccessException,
			InstantiationException, InvocationTargetException {
		BeanInfo beanInfo = Introspector.getBeanInfo(type); // 获取类属性
		Object obj = type.newInstance(); // 创建 JavaBean 对象

		// 给 JavaBean 对象的属性赋值
		PropertyDescriptor[] propertyDescriptors = beanInfo
				.getPropertyDescriptors();
		for (int i = 0; i < propertyDescriptors.length; i++) {
			PropertyDescriptor descriptor = propertyDescriptors[i];
			String propertyName = descriptor.getName();

			if (map.containsKey(propertyName)) {
				// 下面一句可以 try 起来，这样当一个属性赋值失败的时候就不会影响其他属性赋值。
				Object value = map.get(propertyName);

				Object[] args = new Object[1];
				args[0] = value;

				descriptor.getWriteMethod().invoke(obj, args);
			}
		}
		return obj;
	}


	/**
	 * 兼容String转Date的map转对象
	 * @param map
	 * @param obj
	 */
	public static void transMap2Bean(Map<String, String> map, Object obj) {
		try {
			DateConvert dtConverter = new DateConvert();
			ConvertUtilsBean convertUtilsBean = new ConvertUtilsBean();
			convertUtilsBean.deregister(Date.class);
			convertUtilsBean.register(dtConverter, Date.class);
			BeanUtilsBean beanUtilsBean = new BeanUtilsBean(convertUtilsBean,
					new PropertyUtilsBean());
			beanUtilsBean.populate(obj, map);

		} catch (Exception e) {
			e.printStackTrace();
		}
		return;
	}

}
